Description
On the dashboard (esp. all-leagues), let users filter the slate by game state so they don't scroll: All / Live / Recent (finals) / Upcoming. A key cycles the filter; a chip row shows the active one. Applies across leagues in all-leagues view and selects which buckets show in single-league view.
Acceptance Criteria
- #1 A state filter (All/Live/Recent/Upcoming) is cyclable via a key and shown as a chip row under the league tabs
- #2 All-leagues view honors the state filter across every league (live=in-progress, recent=finals, upcoming=scheduled)
- #3 Single-league view shows only the buckets matching the filter
- #4 Cursor/selection stays valid when the filter changes (clamped)
Implementation Notes
Added gameFilter enum (All/Live/Recent/Upcoming) + App.stateFilter; 's' cycles, resets cursor. sections() delegates to filteredSections/leagueSlate when active: pulls games of that state from full window (live keeps client order, recent most-recent-first, upcoming chronological), no favorites pin. stateTabs() chip row under league tabs; dashboard chrome 5->6; footer 's filter' hint. Also fixed bases diamond (equal-width rows so 2nd centers over the gap).
Final Summary
Adds a dashboard state filter so users can see just live games, recent finals, or upcoming fixtures without scrolling (TASK-015). New gameFilter enum + App.stateFilter cycled with 's', shown as a highlighted chip row (stateTabs) beneath the league tabs. When active, sections() routes through filteredSections/leagueSlate: games of the chosen state from the full fetched window, ordered per filter, with the favorites pin dropped so the view is purely that state; all-leagues keeps per-league grouping, single-league collapses to one labeled section. Cursor resets/clamps on change. Bundled a fix for the bases diamond (rows were 5 vs 4 wide, skewing 2nd base off-center โ now equal width). Offline tests for live/recent/upcoming behavior + diamond width; go vet/build + full suite green.